package pubsub

import (
	"fmt"
	"sync"

	"hundsun.com/hsl/hschain/common/types"
	dbm "hundsun.com/hsl/hschain/store/db"
)

// SystemParamManage system param参数管理接口
type SystemParamManage interface {
	// 设置系统参数
	SetSysParam(blockNum int64, param map[string][]byte)
	// 获取当前版本参数
	GetCurSysParam(sysTy string) ([]byte, error)
	// 批量获取上个版本
	GetPreSysParams(blockNum int64, tys []string) ([][]byte, error)
	// 获取上个版本
	GetPreSysParam(blockNum int64, sysTy string) ([]byte, error)
	// 回退情况下删除系统消息
	DelSysParam(blockNum int64, param map[string][]byte) error
}

// SystemParamMgr ...
type SystemParamMgr struct {
	db  dbm.Database // 系统参数需要存储到数据库中
	mut *sync.Mutex
	cur map[string][]byte // 用于缓存系统参数
}

// NewSystemParamMgr ...
func NewSystemParamMgr(ldb dbm.Database) *SystemParamMgr {
	sys := &SystemParamMgr{
		db:  ldb,
		cur: make(map[string][]byte),
		mut: &sync.Mutex{},
	}

	// load consensus from db
	list := ldb.NewList()
	sysParamKey := types.GeneSysParamUpdateKeyPrefix()
	_, vals, err := list.ListPrefix(sysParamKey, 1, dbm.OptionReverse|dbm.OptionWithValue)
	if err != nil && len(vals) > 0 {
		sys.cur[string(sysParamKey)] = vals[0]
	}
	return sys
}

// SetSysParam set system param以blockNum+eventName为key，value为实际参数；类似于做一个多版本记录系统参数
func (s *SystemParamMgr) SetSysParam(blockNum int64, param map[string][]byte) error {
	if len(param) == 0 {
		return nil
	}
	// 先批量保存数据库
	batch := s.db.NewBatch(true)
	for k, v := range param {
		batch.Set(genSysParamKey(blockNum, k), v)
	}
	err := batch.Write()
	if err != nil {
		return err
	}
	// 更新系统当前参数
	s.updateCurSysParam(param)
	return nil
}

// 更新最新的cursysParam
func (s *SystemParamMgr) updateCurSysParam(param map[string][]byte) {
	s.mut.Lock()
	defer s.mut.Unlock()
	for k, v := range param {
		s.cur[k] = v
	}
}

// GetCurSysParam ...
func (s *SystemParamMgr) GetCurSysParam(sysTy string) ([]byte, error) {
	v, err := s.getCurSysParam(sysTy)
	if err == nil {
		return v, nil
	}
	return nil, types.ErrNotFound
}

func (s *SystemParamMgr) getCurSysParam(sysTy string) ([]byte, error) {
	s.mut.Lock()
	defer s.mut.Unlock()
	if s.cur != nil {
		if v, ok := s.cur[sysTy]; ok {
			return v, nil
		}
	}
	return nil, types.ErrNotFound
}

// GetPreSysParams 即获取到版本号<blockNum tys所对应的数据 ,即blockNum的上一个版本
func (s *SystemParamMgr) GetPreSysParams(blockNum int64, tys []string) ([][]byte, error) {
	values := make([][]byte, len(tys))
	var errRe error
	for i, ty := range tys {
		v, err := s.GetPreSysParam(blockNum, ty)
		if err == nil {
			values[i] = v
		} else {
			errRe = err
		}
	}
	return values, errRe
}

// GetPreSysParam 即获取到版本号<blockNum sysTy所对应的数据 ,即blockNum的上一个版本
func (s *SystemParamMgr) GetPreSysParam(blockNum int64, sysTy string) ([]byte, error) {
	// 从数据库中获取
	lst := s.db.NewList()
	_, value, err := lst.ListRange(
		genSysParamKey(0, sysTy),        // key的最小范围
		genSysParamKey(blockNum, sysTy), // key的最大范围
		1,
		dbm.OptionReverse|dbm.OptionWithValue)
	if err != nil || len(value) != 1 {
		return nil, types.ErrNotFound
	}
	return value[0], nil
}

// DelSysParam del system param
func (s *SystemParamMgr) DelSysParam(blockNum int64, param map[string][]byte) error {
	// 1.先批量保存数据库
	batch := s.db.NewBatch(true)
	for k := range param {
		batch.Delete(genSysParamKey(blockNum, k))
	}
	err := batch.Write()
	if err != nil {
		return err
	}
	// 2. 获取上一个版本参数
	preParam := make(map[string][]byte)
	for k := range param {
		value, err := s.GetPreSysParam(blockNum, k)
		if err == nil {
			preParam[k] = value
		}
	}
	// 3. 删除当前版本的参数
	s.delCurSysParam(param)
	// 4. 更新系统上一个版本参数
	s.updateCurSysParam(preParam)
	return nil
}

// 更新最新的cursysParam
func (s *SystemParamMgr) delCurSysParam(param map[string][]byte) {
	s.mut.Lock()
	defer s.mut.Unlock()
	for k := range param {
		delete(s.cur, k)
	}
}

func genSysParamKey(blockNum int64, sysTy string) []byte {
	return []byte(fmt.Sprintf("%s-%18d", sysTy, blockNum))
}
